package com.lianqi.emcpframework.design.support.event;

import java.util.List;
import java.util.function.Function;

import com.lianqi.emcpframework.design.domain.event.*;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;

import lombok.Data;

/**
 * @program emcp-framework
 * @ClassName DefaultDomainEventBus
 * @description: 默认的领域事件总线实现类
 * @author: sky
 * @create: 2019/06/06 19:00
 */
public class DefaultDomainEventBus implements DomainEventBus {

    private final List<RegisterItem> registerItems = Lists.newCopyOnWriteArrayList();

    @Override
    public <ID, EVENT extends DomainEvent> void publish(EVENT event) {
        registerItems.forEach(registerItem -> registerItem.handEvent(event));
    }

    @Override
    public <E extends DomainEvent> void register(DomainEventSubscriber<E> subscriber, DomainEventExecutor executor,
        DomainEventHandler<E> handler) {
        registerItems.add(new RegisterItem(subscriber, executor, handler));
    }

    @Override
    public <E extends DomainEvent> void unregister(DomainEventSubscriber<E> subscriber) {
        doUnregister(registerItem -> registerItem == subscriber);
    }

    @Override
    public <E extends DomainEvent> void unregisterAll(DomainEventHandler<E> handler) {
        doUnregister(registerItem -> registerItem.getHandler() == handler);
    }

    private void doUnregister(Function<RegisterItem, Boolean> checker) {
        Iterators.removeIf(registerItems.iterator(), item -> checker.apply(item));
    }

    /**
     * 领域事件注册元素
     */
    @Data
    private class RegisterItem {
        /**
         * 领域事件订阅者
         */
        private final DomainEventSubscriber subscriber;

        /**
         * 领域事件执行器
         */
        private final DomainEventExecutor executor;

        /**
         * 领域事件handler
         */
        private final DomainEventHandler handler;

        /**
         * 执行领域事件
         * 
         * @param event
         * @param <EVENT>
         */
        public <EVENT extends DomainEvent> void handEvent(EVENT event) {
            if (subscriber.accept(event)) {
                executor.submit(handler, event);
            }
        }
    }
}
